//
//  FileTools.swift
//  Singapore Customer App
//
//  Created by Kiyo on 2021/6/7.
//

import UIKit

public enum AppDirectories {
    case documents
    case library
    case libraryCaches
    case temp
    case customPath(path: FilePathProtocol)
}

private protocol FilePathProtocol {
    func filePathUrl() -> URL
    func stringPath() -> String
}

public struct FilePathTools {
    
    public static func documentsDirectoryURL() -> URL {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    }

    public static func libraryDirectoryURL() -> URL {
        return FileManager.default.urls(for: FileManager.SearchPathDirectory.libraryDirectory, in: .userDomainMask).first!
    }

    public static func tempDirectoryURL() -> URL {
        return FileManager.default.temporaryDirectory
    }

    public static func librayCachesURL() -> URL {
        return FileManager.default.urls(for: FileManager.SearchPathDirectory.cachesDirectory, in: .userDomainMask).first!
    }

    public static func setupFilePath(directory: AppDirectories, name: String) -> URL {
        return getURL(for: directory).appendingPathComponent(name)
    }

    private static func getURL(for directory: AppDirectories) -> URL {
        switch directory {
        case .documents:
            return documentsDirectoryURL()
        case .libraryCaches:
            return librayCachesURL()
        case .library:
            return libraryDirectoryURL()
        case .temp:
            return tempDirectoryURL()
        case .customPath(let path):
            return path.filePathUrl()
        }
    }
}

public struct FileTools {

    /// 创建文件夹
    /// - Parameters:
    ///   - basePath: 沙盒路径
    ///   - folderName: 文件夹名称
    ///   - createIntermediates: 是否创建路径文件夹
    ///   - attributes: attributes
    /// - Throws: 创建失败的错误
    public static func createFolder(basePath: AppDirectories,
                             folderName:String,
                             createIntermediates: Bool = true,
                             attributes: [FileAttributeKey : Any]? = nil) throws {
        
        let filePath = FilePathTools.setupFilePath(directory: basePath, name: folderName)
        let  fileManager = FileManager.default
        do {
            try fileManager.createDirectory(atPath:filePath.path, withIntermediateDirectories: createIntermediates, attributes: attributes)
        } catch {
            JTPrint("❌ createFolder Error: \(error)")
            throw error
        }
    }

    /// 写入文件
    /// - Parameters:
    ///   - content: 写入沙盒的数据
    ///   - filePath: 写入路径
    ///   - options: options
    /// - Throws: 写入错误
    public static func writeFile(content: Data,
                          filePath: FilePathProtocol,
                          options: Data.WritingOptions = []) throws {
        
        do {
            let cryptoData = try content.aesEncrypt()
            try cryptoData.write(to: filePath.filePathUrl(), options: options)
        } catch {
            JTPrint("❌ writeFile Error: \(error)")
            throw error
        }
    }
    
    /// 读取文件
    /// - Parameter filePath: 读取路径
    /// - Throws: 读取错误
    /// - Returns: 读取到的数据
    public static func readFile(filePath: FilePathProtocol) throws -> Data {
        
        let fileContents = FileManager.default.contents(atPath: filePath.filePathUrl().path)
        guard let data = fileContents, !data.isEmpty else { throw JTCryptoError.DecryptFail }
        
        do {
            return try data.aesDecrypt()
        } catch {
            JTPrint("❌ readFile Error: \(error)")
            throw JTCryptoError.DecryptFail
        }
    }
    
    /// 删除文件
    /// - Parameter filePath: 删除路径
    /// - Throws: 删除失败
    public static func deleteFile(filePath: FilePathProtocol) throws {
        do {
            try FileManager.default.removeItem(at: filePath.filePathUrl())
        } catch {
            JTPrint("❌ deleteFile Error: \(error)")
            throw error
        }
    }
    
    /// 重命名文件
    /// - Parameters:
    ///   - path: 路径
    ///   - oldName: 旧文件名
    ///   - newName: 新文件名
    /// - Throws: 重命名失败
    public static func renameFile(path: AppDirectories, oldName: String, newName: String) throws {
        let oldPath = FilePathTools.setupFilePath(directory: path, name: oldName)
        let newPath = FilePathTools.setupFilePath(directory: path, name: newName)
        do {
            try FileManager.default.moveItem(at: oldPath, to: newPath)
        } catch {
            JTPrint("❌ renameFile Error: \(error)")
            throw error
        }
    }
    
    /// 移动文件
    /// - Parameters:
    ///   - fileName: 文件名称
    ///   - fromDirectory: 移动前路径
    ///   - toDirectory: 移动后路径
    /// - Throws: 移动失败
    public static func moveFile(fileName: String, fromDirectory: String, toDirectory: String) throws {
        let originURL = FilePathTools.setupFilePath(directory: .customPath(path: fromDirectory), name: fileName)
        let destinationURL = FilePathTools.setupFilePath(directory: .customPath(path: toDirectory), name: fileName)
        do {
            try FileManager.default.moveItem(at: originURL, to: destinationURL)
        } catch {
            JTPrint("❌ moveFile Error: \(error)")
            throw error
        }
    }

    /// 拷贝文件
    /// - Parameters:
    ///   - fileName: 文件名
    ///   - fromDirectory: 拷贝前路径
    ///   - toDirectory: 拷贝后路径
    /// - Throws: 拷贝失败
    public static func copyFile(fileName: String, fromDirectory: String, toDirectory: String) throws {
        let originURL = FilePathTools.setupFilePath(directory: .customPath(path: fromDirectory), name: fileName)
        let destinationURL = FilePathTools.setupFilePath(directory: .customPath(path: toDirectory), name: fileName)
        return try FileManager.default.copyItem(at: originURL, to: destinationURL)
    }

    /// 是否可以写入
    /// - Parameter fileURL: 路径
    /// - Returns: 是否可以写入
    public static func isWritable(fileURL: FilePathProtocol) -> Bool {
        if FileManager.default.isWritableFile(atPath: fileURL.stringPath()) {
            return true
        } else {
            return false
        }
    }
    
    /// 是否可读
    /// - Parameter filePath: 路径
    /// - Returns: 是否可读
    public static func isReadable(filePath: FilePathProtocol) -> Bool {
        if FileManager.default.isReadableFile(atPath: filePath.stringPath()) {
            return true
        } else {
            return false
        }
    }
    
    /// 是否存在
    /// - Parameter filePath: 路径
    /// - Returns: 是否存在
    public static func exists(filePath: FilePathProtocol) -> Bool {
        if FileManager.default.fileExists(atPath: filePath.stringPath()) {
            return true
        } else {
            return false
        }
    }

    /// 获取制定文件夹下所有文件名
    /// - Parameter folderPath: 路径
    /// - Throws: 获取失败
    /// - Returns: 文件名数组
    public static func getFilePathList(folderPath: FilePathProtocol) throws -> [String] {
        let fileManager = FileManager.default
        do {
            return try fileManager.contentsOfDirectory(atPath: folderPath.stringPath())
        } catch {
            JTPrint("getFilePathList Error: \(error)")
            throw error
        }
    }
}

extension String: FilePathProtocol {
    private func stringPath() -> String {
        return self
    }
    private func filePathUrl() -> URL {
        return URL(fileURLWithPath: self)
    }
}

extension URL: FilePathProtocol {
    private func stringPath() -> String {
        return path
    }
    
    private func filePathUrl() -> URL {
        return self
    }
}
